package main

import (
	"bufio"
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/elastic/go-elasticsearch/v8"
	"github.com/elastic/go-elasticsearch/v8/esapi"
)

// Document represents a sample document structure
type Document struct {
	ID      string `json:"id"`
	Title   string `json:"title"`
	Content string `json:"content"`
	Created int64  `json:"created"`
}

// ESConfig 存储Elasticsearch配置信息
type ESConfig struct {
	Addresses []string
	Username  string
	Password  string
}

// loadESConfig 从配置文件中加载Elasticsearch配置
func loadESConfig(filename string) (*ESConfig, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, fmt.Errorf("无法打开配置文件 %s: %v", filename, err)
	}
	defer file.Close()

	config := &ESConfig{
		Addresses: make([]string, 0),
	}

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		// 跳过空行和注释行
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}

		parts := strings.SplitN(line, "=", 2)
		if len(parts) != 2 {
			continue
		}

		key := strings.TrimSpace(parts[0])
		value := strings.TrimSpace(parts[1])

		switch key {
		case "address":
			config.Addresses = append(config.Addresses, value)
		case "username":
			config.Username = value
		case "password":
			config.Password = value
		}
	}

	if err := scanner.Err(); err != nil {
		return nil, fmt.Errorf("读取配置文件出错: %v", err)
	}

	// 检查必需的配置项
	if len(config.Addresses) == 0 {
		return nil, fmt.Errorf("配置文件中缺少Elasticsearch地址")
	}

	return config, nil
}

// VerifyElasticsearch 执行 Elasticsearch 集群验证
func VerifyElasticsearch() error {
	// 从配置文件读取ES配置
	config, err := loadESConfig("es-information.conf")
	if err != nil {
		return fmt.Errorf("加载配置失败: %s", err)
	}

	// 创建ES客户端，忽略证书验证
	cfg := elasticsearch.Config{
		Addresses: config.Addresses,
		Username:  config.Username,
		Password:  config.Password,
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true, // 忽略证书验证
			},
		},
	}

	es, err := elasticsearch.NewClient(cfg)
	if err != nil {
		return fmt.Errorf("创建客户端失败: %s", err)
	}

	// 测试连接
	res, err := es.Info()
	if err != nil {
		return fmt.Errorf("连接测试失败: %s", err)
	}
	defer res.Body.Close()
	fmt.Println("✓ ES集群连接成功")

	// 读取响应体
	var info map[string]interface{}
	if err := json.NewDecoder(res.Body).Decode(&info); err != nil {
		return fmt.Errorf("解析响应失败: %s", err)
	}
	fmt.Printf("✓ 集群信息: %s\n", info["tagline"])

	// 索引名称
	indexName := "test_index_" + fmt.Sprintf("%d", time.Now().Unix())

	// 1. 创建索引
	fmt.Println("\n=== 创建索引 ===")
	if err := createIndex(es, indexName); err != nil {
		return fmt.Errorf("创建索引失败: %s", err)
	}
	fmt.Println("✓ 索引创建成功")

	// 2. 插入数据
	fmt.Println("\n=== 插入数据 ===")
	doc := Document{
		ID:      "1",
		Title:   "测试文档",
		Content: "这是一个测试文档的内容",
		Created: time.Now().Unix(),
	}

	if err := insertDocument(es, indexName, doc.ID, doc); err != nil {
		return fmt.Errorf("插入数据失败: %s", err)
	}
	fmt.Println("✓ 数据插入成功")

	// 等待数据刷新
	time.Sleep(1 * time.Second)

	// 3. 查询数据
	fmt.Println("\n=== 查询数据 ===")
	result, err := searchDocument(es, indexName, doc.ID)
	if err != nil {
		return fmt.Errorf("查询数据失败: %s", err)
	}
	fmt.Printf("✓ 查询结果: %+v\n", result)

	// 4. 删除数据
	fmt.Println("\n=== 删除数据 ===")
	if err := deleteDocument(es, indexName, doc.ID); err != nil {
		return fmt.Errorf("删除数据失败: %s", err)
	}
	fmt.Println("✓ 数据删除成功")

	// 5. 删除索引
	fmt.Println("\n=== 删除索引 ===")
	if err := deleteIndex(es, indexName); err != nil {
		return fmt.Errorf("删除索引失败: %s", err)
	}
	fmt.Println("✓ 索引删除成功")

	fmt.Println("\n=== 所有操作完成 ===")
	return nil
}

// createIndex 创建索引
func createIndex(es *elasticsearch.Client, indexName string) error {
	// 定义索引映射
	mapping := `{
		"settings": {
			"number_of_shards": 1,
			"number_of_replicas": 0
		},
		"mappings": {
			"properties": {
				"id": {
					"type": "keyword"
				},
				"title": {
					"type": "text"
				},
				"content": {
					"type": "text"
				},
				"created": {
					"type": "long"
				}
			}
		}
	}`

	req := esapi.IndicesCreateRequest{
		Index: indexName,
		Body:  strings.NewReader(mapping),
	}

	res, err := req.Do(context.Background(), es)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	if res.StatusCode >= 400 {
		return fmt.Errorf("创建索引失败，状态码: %d", res.StatusCode)
	}

	return nil
}

// insertDocument 插入文档
func insertDocument(es *elasticsearch.Client, indexName string, docID string, doc Document) error {
	data, err := json.Marshal(doc)
	if err != nil {
		return err
	}

	req := esapi.IndexRequest{
		Index:      indexName,
		DocumentID: docID,
		Body:       strings.NewReader(string(data)),
		Refresh:    "true",
	}

	res, err := req.Do(context.Background(), es)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	if res.StatusCode >= 400 {
		return fmt.Errorf("插入文档失败，状态码: %d", res.StatusCode)
	}

	return nil
}

// searchDocument 查询文档
func searchDocument(es *elasticsearch.Client, indexName string, docID string) (map[string]interface{}, error) {
	req := esapi.GetRequest{
		Index:      indexName,
		DocumentID: docID,
	}

	res, err := req.Do(context.Background(), es)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	if res.StatusCode == 404 {
		return nil, fmt.Errorf("文档未找到")
	}

	if res.StatusCode >= 400 {
		return nil, fmt.Errorf("查询文档失败，状态码: %d", res.StatusCode)
	}

	var result map[string]interface{}
	if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
		return nil, err
	}

	return result, nil
}

// deleteDocument 删除文档
func deleteDocument(es *elasticsearch.Client, indexName string, docID string) error {
	req := esapi.DeleteRequest{
		Index:      indexName,
		DocumentID: docID,
		Refresh:    "true",
	}

	res, err := req.Do(context.Background(), es)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	if res.StatusCode >= 400 {
		return fmt.Errorf("删除文档失败，状态码: %d", res.StatusCode)
	}

	return nil
}

// deleteIndex 删除索引
func deleteIndex(es *elasticsearch.Client, indexName string) error {
	req := esapi.IndicesDeleteRequest{
		Index: []string{indexName},
	}

	res, err := req.Do(context.Background(), es)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	if res.StatusCode >= 400 {
		return fmt.Errorf("删除索引失败，状态码: %d", res.StatusCode)
	}

	return nil
}
